﻿// Copyright (c)  YISH. All rights reserved.
// Licensed under the MIT License, See License.txt in the project root for license information.

using System;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Text;
using JetBrains.Annotations;
using NSQL.Expressions;
using NSQL.Expressions.Functions;
using NSQL.Extensions;
using NSQL.Resource;
using NSQL.Statements;
using NSQL.Variables;
#if NETSTANDARD1_6 || NETSTANDARD2_0
using NSQL.Sharpen;
#endif

namespace NSQL.Generator
{
    [SuppressMessage("ReSharper", "MemberCanBePrivate.Global")]
    [SuppressMessage("ReSharper", "IdentifierTypo")]
    [SuppressMessage("ReSharper", "VirtualMemberNeverOverridden.Global")]
    public abstract class BaseGenerator : ISqlGenerator, IVisitor<StringBuilder>
    {
        private readonly KeyWords _;
        [field: ThreadStatic]
        protected static VariableSpace Variable { get; private set; }
        protected SqlGeneratorSettings Settings { get; }
        protected IDatabaseInfo DatabaseInfo => Settings.Database;
        protected bool Optimized => Settings.Optimized;
        protected LetterCase LetterCase => Settings.LetterCase;
        protected bool BraceJoin => Settings.BraceJoin;

        protected virtual VariableSpace InitNewSpace() => new VariableSpace(Settings, Variable);

        protected void Enter()
        {
            Variable = InitNewSpace();
        }

        [SuppressMessage("ReSharper", "MemberCanBeMadeStatic.Global")]
        protected void Exit()
        {
            Variable = Variable?.ParentSpace;
        }

        protected BaseGenerator(SqlGeneratorSettings settings, KeyWords keyWords)
        {
            Settings = settings ?? throw new ArgumentNullException(nameof(settings));
            _ = keyWords;
        }
        
        public string Generate(IStatement statement) => statement.Accept(this);

        public string Generate(ISelectStatement statement)
        {
            Enter();
            try
            {
                while (true)
                {
                    if (Optimized && statement.CanOptimized)
                    {
                        Debug.Assert(statement.Elements.Count == 1);
                        Debug.Assert(statement.TableSources.Count == 1);
                        Debug.Assert(statement.TableSources[0] is ISelectStatement);
                        Debug.Assert(statement.Skip <= 0);
                        Debug.Assert(statement.Limit <= 0);
                        Debug.Assert(statement.Predicate is null);
                        statement = (ISelectStatement) statement.TableSources[0];
                        continue;
                    }

                    var from = BuildFrom(statement.TableSources);
                    var where = BuildWhere(statement.Predicate);

                    StringBuilder group;
                    StringBuilder having;
                    if (statement.GroupClause != null)
                    {
                        group = BuildGroup(statement.GroupClause);

                        having = statement.GroupClause.Having is null ? null : BuilderHaving(statement.GroupClause.Having);
                    }
                    else
                    {
                        group = null;
                        having = null;
                    }


                    var order = BuilderOrder(statement.SortingClause);
                    var select = BuildSelect(statement.Elements);
                    return FormatSelect(select, from, where, group, having, order, statement.Skip, statement.Limit).ToString();
                }
            }
            finally
            {
                Exit();
            }
        }

        string ISqlGenerator.Generate(UnionStatement statement)
        {
            var builder = new StringBuilder();
            if (statement.Left.HasPaging())
            {
                
                builder.Append('(').Append(statement.Left.Accept(this)).Append(')');
            }
            else
            {
                builder.Append(statement.Left.Accept(this));
            }
            builder.Append(' ').Append(_.Union);
            if (statement.Type == UnionType.All)
            {
                builder.Append(' ').Append(_.All);
            }
            builder.Append(' ');
            if (statement.Right.HasPaging())
            {
                builder.Append('(').Append(statement.Right.Accept(this)).Append(')');
            }
            else
            {
                
                builder.Append(statement.Right.Accept(this));
            }

            return FormatPaging(builder, statement.Skip, statement.Limit).ToString();
        }

        string ISqlGenerator.Generate(IntersectionStatement statement)
        {
            var builder = new StringBuilder();
            if (statement.Left.HasPaging())
            {
                builder.Append('(').Append(statement.Left.Accept(this)).Append(')');
            }
            else
            {
                builder.Append(statement.Left.Accept(this));
            }
            builder.Append(' ').Append(_.Intersect).Append(' ');
            if (statement.Right.HasPaging())
            {
                builder.Append('(').Append(statement.Right.Accept(this)).Append(')');
            }
            else
            {
                builder.Append(statement.Right.Accept(this));
            }
            return FormatPaging(builder, statement.Skip, statement.Limit).ToString();
        }

        string ISqlGenerator.Generate(MinusStatement statement)
        {
            var builder = new StringBuilder();
            if (statement.Left.HasPaging())
            {
                builder.Append('(').Append(statement.Left.Accept(this)).Append(')');
            }
            else
            {
                builder.Append(statement.Left.Accept(this));
            }
            builder.Append(' ').Append(_.Minus).Append(' ');
            if (statement.Right.HasPaging())
            {
                builder.Append('(').Append(statement.Right.Accept(this)).Append(')');
            }
            else
            {
                builder.Append(statement.Right.Accept(this));
            }
            return FormatPaging(builder, statement.Skip, statement.Limit).ToString();
        }

        string ISqlGenerator.Generate(IExpression expression)
        {
            return expression.Accept(this).ToString();
        }

        private string Generate(ISubquery subquery)
        {
            if (subquery is IStatement statement)
            {
                return statement.Accept(this);
            }
            return null;
        }

        protected virtual StringBuilder WrapIdentifier(StringBuilder builder, Identifier identifier, bool delimited = false)
        {
            if (identifier != "*" && (identifier.Delimited ||delimited))
            {
                return builder.Append(Settings.BeginDelimiter).Append(identifier).Append(Settings.EndDelimiter);
            }
            return builder.Append(identifier);
        }
        
        #region [Select]
        private StringBuilder BuildSelect(SelectElementCollection selectElements, StringBuilder builder = null)
        {
            builder = builder ?? new StringBuilder();

            using (var enumerator = selectElements.GetEnumerator())
            {
                if (!enumerator.MoveNext()) return builder;
                var current = enumerator.Current;
                if (current != null)
                {
                    if (selectElements.IsDistinct)
                    {
                        builder.Append(_.Distinct).Append(' ');
                    }
                    BuildSelectElement(current, builder);
                }
                while (enumerator.MoveNext())
                {
                    current = enumerator.Current;
                    if (current == null) continue;
                    builder.Append(", ");
                    BuildSelectElement(current, builder);
                }

            }
            return builder;
        }

        // ReSharper disable once UnusedMethodReturnValue.Local
        private StringBuilder BuildSelectElement(SelectElement selectElement, StringBuilder builder = null)
        {
            builder = builder ?? new StringBuilder();
            if (selectElement == SelectElement.Asterisk)
            {
                builder.Append('*');
            }
            else
            {
                builder.Append(selectElement.Element.Accept(this));
            }

            if (selectElement.Alias is null) return builder;

            builder.Append(' ').Append(_.As);
            builder.Append(' ');

            WrapIdentifier(builder, selectElement.Alias, selectElement.Alias.Delimited || Settings.Delimited);
            return builder;
        }

        
        protected virtual StringBuilder FormatSelect(StringBuilder select, StringBuilder from, StringBuilder where,
            StringBuilder group, StringBuilder having, StringBuilder order)
        {
            var sb = new StringBuilder();
            sb.Append(_.Select).Append(' ');

            if (select == null || select.Length == 0)
            {
                sb.Append('1');
            }
            else
            {
                sb.Append(select);
            }

            if (from?.Length > 0)
            {
                sb.Append(' ').Append(_.From).Append(' ').Append(from);
            }

            if (where?.Length > 0)
            {
                sb.Append(' ').Append(_.Where).Append(' ').Append(where);
            }

            if (group?.Length > 0)
            {
                sb.Append(' ').Append(_.Group).Append(' ').Append(_.By).Append(' ').Append(group);
            }
            
            
            if (having?.Length > 0)
            {
                sb.Append(' ').Append(_.Having).Append(' ').Append(having);
            }

            if (order?.Length > 0)
            {
                sb.Append(' ').Append(_.Order).Append(' ').Append(_.By).Append(' ').Append(order);
            }

            return sb;
        }

        protected virtual StringBuilder FormatSelect(StringBuilder select, StringBuilder from, StringBuilder where,
            StringBuilder group, StringBuilder having, StringBuilder order, int skip, int limit)
        {
            var sqlBuilder = FormatSelect(select, from, where, group, having, order);
            return FormatPaging(sqlBuilder, skip, limit);
        }

        protected virtual StringBuilder FormatPaging([NotNull]StringBuilder statement, int skip, int limit)
        {
            if (skip > 0)
            {
                statement.Append(' ');
                statement.Append(_.Skip);
                statement.Append(' ');
                statement.Append(skip);
            }
            
            if (limit <= 0) return statement;
            statement.Append(' ');
            statement.Append(_.Limit);
            statement.Append(' ');
            statement.Append(limit);
            return statement;
        }
        #endregion

        #region [From]

        private StringBuilder BuildFrom(TableSourceCollection tableSources, StringBuilder builder = null)
        {
            if (tableSources.Count == 1 && tableSources[0] is ITable t && t.IsDual)
            {
                return null;
            }

            Variable.SetTableCount(tableSources.TableCount);
            return (builder ?? new StringBuilder()).AppendJoin(", ", tableSources.Select(o => BuildFrom(o)));
        }
        
        private StringBuilder BuildFrom(IJoinable table, StringBuilder builder = null)
        {
            builder = builder ?? new StringBuilder();
            switch (table)
            {
                case ITable tableSource:
                    if (!string.IsNullOrWhiteSpace(tableSource.Schema))
                    {
                        WrapIdentifier(builder, tableSource.Schema);
                        builder.Append('.');
                    }
                    var tableInfo = Variable.AppendTable(new TableInfo
                    {
                        Name = tableSource.Name,
                        Alias = tableSource.Alias
                    });
                    
                    WrapIdentifier(builder, tableInfo.Name);
                    
                    if (!string.IsNullOrEmpty(tableSource.Alias))
                    {
                        builder.Append(' ');

                        WrapIdentifier(builder, tableSource.Alias, tableSource.Alias.Delimited || Settings.Delimited);
                    }
                    else if (!string.IsNullOrWhiteSpace(tableInfo.Alias))
                    {
                        builder.Append(' ');
                        WrapIdentifier(builder, tableInfo.Alias);
                    }
                    break;
                case ISubquery subquery:
                    builder.Append('(').Append(Generate(subquery)).Append(')');

                    if (!string.IsNullOrEmpty(subquery.Alias))
                    {
                        builder.Append(' ');

                        WrapIdentifier(builder, subquery.Alias, subquery.Alias.Delimited || Settings.Delimited);
                    }
                    break;
                case IJoinNode joinNode:
                    builder.Append(BuildFrom(joinNode.Base));
                    builder.Append(' ');
                    switch (joinNode.Type)
                    {
                        case JoinType.Inner:
                            builder.Append(_.Inner);
                            break;
                        case JoinType.Left:
                            builder.Append(_.Left);
                            break;
                        case JoinType.Right:
                            builder.Append(_.Right);
                            break;
                        case JoinType.CrossJoin:
                            builder.Append(_.Cross);
                            break;
                        case JoinType.FullJoin:
                            builder.Append(_.Full);
                            break;
                        case JoinType.LeftOuter:
                            builder.Append(_.Left).Append(' ').Append(_.Outer);
                            break;
                        case JoinType.RightOuter:
                            builder.Append(_.Right).Append(' ').Append(_.Outer);
                            break;
                        default:
                            throw new ArgumentOutOfRangeException();
                    }

                    builder.Append(' ').Append(_.Join).Append(' ');
                    builder.Append(BuildFrom(joinNode.JoinTable));
                    
                    builder.Append(' ').Append(_.On).Append(' ');
                    if (BraceJoin)
                    {
                        builder.Append('(');
                    }
                    builder.Append(joinNode.Condition?.Accept(this));
                    if (BraceJoin)
                    {
                        builder.Append(')');
                    }
                    return builder;
                    
                default:
                    throw new NotImplementedException();
            }

            return builder;
        }

        #endregion

        #region [Where]
        // ReSharper disable once SuggestBaseTypeForParameter
        private StringBuilder BuildWhere(IPredicateExpression expression) => expression?.Accept(this);
        #endregion

        #region [Group]
        private StringBuilder BuildGroup(GroupCollection expressions, StringBuilder builder = null)
        {
            builder = builder ?? new StringBuilder();
            builder.AppendJoin(", ", expressions.Select(o => o.Accept(this)));
            return builder;
        }
        #endregion

        #region [Order]

        private StringBuilder BuilderOrder(SortingCollection sortingCollection, StringBuilder builder = null)
        {
            builder = builder ?? new StringBuilder();
            using (var enumerator = sortingCollection.GetEnumerator())
            {
                if (!enumerator.MoveNext()) return builder;
                var current = enumerator.Current;
                BuilderOrder(current, builder);

                while (enumerator.MoveNext())
                {
                    current = enumerator.Current;
                    if (current == null) continue;
                    builder.Append(", ");
                    BuilderOrder(current, builder);
                }
            }
            return builder;
        }

        // ReSharper disable once MemberCanBeMadeStatic.Local
        // ReSharper disable once UnusedMethodReturnValue.Local
        private StringBuilder BuilderOrder(ISortingElement sortingElement, StringBuilder builder = null)
        {
            if (sortingElement is null) return builder;
            builder = builder ?? new StringBuilder();
            builder.Append(sortingElement.Element.Accept(this));
            switch (sortingElement.Direction)
            {
                case SortingDirection.None:
                    break;
                case SortingDirection.Ascending:
                    builder.Append(' ').Append(_.Asc);
                    break;
                case SortingDirection.Descending:
                    builder.Append(' ').Append(_.Desc);
                    break;
                default:
                    throw new ArgumentOutOfRangeException();
            }
            return builder;
        }

        #endregion
        
        #region [Having]

        // ReSharper disable once SuggestBaseTypeForParameter
        private StringBuilder BuilderHaving(IPredicateExpression predicateExpression)
        {
            return predicateExpression?.Accept(this);
        }

        #endregion

        #region [Expression]

        StringBuilder IExpressionVisitor<StringBuilder>.Visit(IComparisonExpression comparisonExpression)
        {
            var builder = new StringBuilder();
            builder.Append(comparisonExpression.Left.Accept(this));
            builder.Append(' ');
            switch (comparisonExpression.Operator)
            {
                case ComparisonOperator.Equal:
                    builder.Append('=');
                    break;
                case ComparisonOperator.NotEqual:
                    builder.Append("!=");
                    break;
                case ComparisonOperator.Less:
                    builder.Append('<');
                    break;
                case ComparisonOperator.Greater:
                    builder.Append('>');
                    break;
                case ComparisonOperator.LessOrEqual:
                    builder.Append("<=");
                    break;
                case ComparisonOperator.GreaterOrEqual:
                    builder.Append(">=");
                    break;
                default:
                    throw new ArgumentOutOfRangeException();
            }
            builder.Append(' ');
            if (Variable?.ParentSpace != null && comparisonExpression.Left.ToString() == comparisonExpression.Right.ToString())
            {
                var columns = comparisonExpression.Right.AsEnumerable().OfType<IColumnExpression>().ToList();
                try
                {
                    foreach (var columnExpression in columns)
                    {
                        Variable.UpLevel(columnExpression);
                    }
                    builder.Append(comparisonExpression.Right.Accept(this));
                }
                finally
                {
                    foreach (var columnExpression in columns)
                    {
                        Variable.DownLevel(columnExpression);
                    }
                }
            }
            else
            {
                builder.Append(comparisonExpression.Right.Accept(this));
            }
            return builder;
        }

        StringBuilder IExpressionVisitor<StringBuilder>.Visit(IColumnExpression columnExpression)
        {
            var builder = new StringBuilder();
            var columnName = columnExpression.Name;

            if (columnExpression.Table != null)
            {
                WrapIdentifier(builder, columnExpression.Table).Append('.');
            }
            
            var column = DatabaseInfo?.FindColumn(columnExpression);

            // ReSharper disable once InvertIf
            if (column != null)
            {
                if (!ReferenceEquals(column.Expr, null))
                {
                    return column.Expr.Accept(this);
                }

                columnName = column.Name;

                // ReSharper disable once InvertIf
                if (columnExpression.Table is null && column.Table != null)
                {
                    var tables = Variable.GetAllTables().ToList();
                    
                    var tableName = column.Table.Name;

                    var table =
                        (Variable.NeedUpLevel(columnExpression) ? Variable.ParentSpace : Variable).GetTable(
                            tableName);
                    
                    // ReSharper disable once InvertIf
                    if (tables.Count > 1 || table.Alias != null && table.Alias != tableName)
                    {

                        tableName = string.IsNullOrWhiteSpace(table.Alias) ? table.Name : table.Alias;
                        

                        WrapIdentifier(builder, tableName, Settings.Delimited);
                        
                        builder.Append('.');
                    }
                }
            }
            
            return WrapIdentifier(builder, columnName, Settings.Delimited);
        }

        StringBuilder IExpressionVisitor<StringBuilder>.Visit(IConstantExpression constantExpression)
        {
            var builder = new StringBuilder();
            var value = constantExpression.ValueOf();
            switch (value)
            {
                case null:
                    builder.Append("<null>");
                    break;
                case string str:
                    builder.Append('\'');
                    builder.Append(str);
                    builder.Append('\'');
                    break;
                case DateTime dateTime:
                    builder.Append('#');
                    builder.Append(dateTime.ToString(Settings.CultureInfo));
                    builder.Append('#');
                    break;
                case bool b:
                    return new StringBuilder(b ? "True" : "False");
                default:
                    return new StringBuilder(value.ToString());
            }
            return builder;
        }

        StringBuilder IExpressionVisitor<StringBuilder>.Visit(IInExpression inExpression)
        {
            return new StringBuilder($"{inExpression.Left.Accept(this)} In ({inExpression.Elements.Accept(this)})");
        }

        StringBuilder IExpressionVisitor<StringBuilder>.Visit(INotInExpression notInExpression)
        {
            return new StringBuilder($"{notInExpression.Left.Accept(this)} Not In ({notInExpression.Elements.Accept(this)})");
        }

        StringBuilder IExpressionVisitor<StringBuilder>.Visit(ILogicalExpression logicalExpression)
        {
            if (!logicalExpression.IsValid) return null;
            var builder = logicalExpression.Left.Accept(this);
            builder.Append(' ').Append(_.FormatLetter(logicalExpression.Operator.ToString())).Append(' ');
            
            var isGroupExpression = logicalExpression.Right is ILogicalExpression;
            if (isGroupExpression) builder.Append('(');
            builder.Append(logicalExpression.Right.Accept(this));
            if (isGroupExpression) builder.Append(')');
            return builder;
        }

        StringBuilder IExpressionVisitor<StringBuilder>.Visit(IBetweenExpression betweenExpression)
        {
            var builder = betweenExpression.Left.Accept(this);
            builder.Append(' ').Append(_.Between);
            builder.Append(' ').Append(betweenExpression.Begin.Accept(this));
            builder.Append(' ').Append(_.And);
            builder.Append(' ').Append(betweenExpression.End.Accept(this));
            return builder;
        }

        StringBuilder IExpressionVisitor<StringBuilder>.Visit(INotExpression notExpression)
        {
            var builder = new StringBuilder();
            builder.Append(_.Not);
            var isGroupExpression = notExpression.Expr is ILogicalExpression;
            if (isGroupExpression)
            {
                builder.Append('(');
            }
            else
            {
                builder.Append(' ');
            }
            builder.Append(notExpression.Expr.Accept(this));
            if (isGroupExpression) builder.Append(')');
            return builder;
        }

        StringBuilder IExpressionVisitor<StringBuilder>.Visit(INullExpression nullExpression)
        {
            var builder = nullExpression.Expr.Accept(this);
            builder.Append(' ').Append(_.Is);
            builder.Append(' ').Append(_.Null);
            return builder;
        }

        StringBuilder IExpressionVisitor<StringBuilder>.Visit(INotNullExpression notNullExpression)
        {
            var builder = notNullExpression.Expr.Accept(this);
            builder.Append(' ').Append(_.Is);
            builder.Append(' ').Append(_.Not);
            builder.Append(' ').Append(_.Null);
            return builder;
        }

        StringBuilder IExpressionVisitor<StringBuilder>.Visit(IMatchExpression matchExpression)
        {
            var builder = new StringBuilder();
            builder.Append(_.Case);
            foreach (var match in matchExpression.Matches)
            {
                builder.Append(' ').Append(_.When);
                builder.Append(' ').Append(match.When.Accept(this));
                builder.Append(' ').Append(_.Then);
                builder.Append(' ').Append(match.Then.Accept(this));
            }

            builder.Append(' ').Append(_.Else);
            builder.Append(' ').Append(matchExpression.Default.Accept(this));
            builder.Append(' ').Append(_.End);
            return builder;
        }

        StringBuilder IExpressionVisitor<StringBuilder>.Visit(ISimpleMatchExpression simpleMatchExpression)
        {
            var builder = new StringBuilder();
            builder.Append(_.Case);
            builder.Append(' ').Append(simpleMatchExpression.Comparison.Accept(this));
            foreach (var match in simpleMatchExpression.Matches)
            {
                builder.Append(' ').Append(_.When);
                builder.Append(' ').Append(match.When.Accept(this));
                builder.Append(' ').Append(_.Then);
                builder.Append(' ').Append(match.Then.Accept(this));
            }

            builder.Append(' ').Append(_.Else);
            builder.Append(' ');
            if (simpleMatchExpression.Default is null)
            {
                builder.Append(_.Null);
            }
            else
            {
                builder.Append(simpleMatchExpression.Default.Accept(this));
            }
            builder.Append(' ').Append(_.End);
            return builder;
        }

        StringBuilder IExpressionVisitor<StringBuilder>.Visit(IArithmeticExpression arithmeticExpression)
        {
            var builder = new StringBuilder();

            if (Optimized && arithmeticExpression.TryEval(out var result))
            {
                return result.Accept(this);
            }

            if (arithmeticExpression.Left is IArithmeticExpression leftExpression &&
                (arithmeticExpression.Operator == ArithmeticOperator.Multiply || arithmeticExpression.Operator == ArithmeticOperator.Divide) &&
                (leftExpression.Operator == ArithmeticOperator.Plus || leftExpression.Operator == ArithmeticOperator.Minus))
            {
                builder.Append('(').Append(arithmeticExpression.Left?.Accept(this)).Append(')');
            }
            else
            {
                
                builder.Append(arithmeticExpression.Left?.Accept(this));
            }

            switch (arithmeticExpression.Operator)
            {
                case ArithmeticOperator.Plus:
                    if (Optimized &&
                        arithmeticExpression.Right is IConstantNumericExpression constantNumeric0 && constantNumeric0.IsZero)
                    {
                        return builder;
                    }
                    builder.Append(" + ");
                    break;
                case ArithmeticOperator.Minus:
                    if (Optimized &&
                        arithmeticExpression.Right is IConstantNumericExpression constantNumeric1 && constantNumeric1.IsZero)
                    {
                        return builder;
                    }
                    builder.Append(" - ");
                    break;
                case ArithmeticOperator.Multiply:
                    builder.Append(" * ");
                    break;
                case ArithmeticOperator.Divide:
                    builder.Append(" / ");
                    break;
                default:
                    throw new ArgumentOutOfRangeException();
            }

            if (arithmeticExpression.Right is IArithmeticExpression rightExpression &&
                (rightExpression.Operator == ArithmeticOperator.Plus || rightExpression.Operator == ArithmeticOperator.Minus))
            {
                return builder.Append('(').Append(arithmeticExpression.Right.Accept(this)).Append(')');
            }

            return builder.Append(arithmeticExpression.Right.Accept(this));
        }

        public StringBuilder Visit(IUnaryExpression unaryExpression)
        {
            var builder = new StringBuilder();
            switch (unaryExpression.Operator)
            {
                case UnaryOperator.Plus:
                    builder.Append('+');
                    break;
                case UnaryOperator.Minus:
                    builder.Append('-');
                    break;
                default:
                    throw new ArgumentOutOfRangeException();
            }

            builder.Append(unaryExpression.Accept(this));
            return builder;
        }

        public StringBuilder Visit(ISystemVariable systemVariable)
        {
            switch (systemVariable)
            {
                case NowFunction _:
                    return new StringBuilder(_.Now);
                default:
                    throw new NotImplementedException();
            }
        }

        StringBuilder IExpressionVisitor<StringBuilder>.Visit(ILikeExpression likeExpression)
        {
            var builder = likeExpression.Left.Accept(this);
            builder.Append(' ').Append(_.Like);
            builder.Append(' ').Append(likeExpression.Pattern.Accept(this));
            return builder;
        }

        StringBuilder IExpressionVisitor<StringBuilder>.Visit(IExpressions inElements)
        {
            var builder = new StringBuilder();
            using (var enumerator = inElements.GetEnumerator())
            {
                if (!enumerator.MoveNext())
                {
                    throw new Exception("Append [IExpressions] error.");
                }

                var current = enumerator.Current;
                if (current != null)
                {
                    builder.Append(current.Accept(this));
                }

                while (enumerator.MoveNext())
                {
                    current = enumerator.Current;
                    if (current == null) continue;
                    builder.Append(", ");
                    builder.Append(current.Accept(this));
                }
            }

            return builder;
        }

        public StringBuilder Visit(IExistsExpression existsExpression)
        {
            return new StringBuilder()
                .Append(_.Exists)
                .Append('(')
                .Append(Generate(existsExpression.Subquery))
                .Append(')');
        }

        #region [Function]

        IFunctionVisitor<StringBuilder> IExpressionVisitor<StringBuilder>.FunctionVisitor => this;

        StringBuilder IFunctionVisitor<StringBuilder>.Visit(ICountFunction countFunction)
        {
            var builder = new StringBuilder();
            builder.Append(_.Count);
            builder.Append('(');
            switch (countFunction.AggregateType)
            {
                case AggregateType.None:
                    break;
                case AggregateType.All:
                    builder.Append(_.All).Append(' ');
                    break;
                case AggregateType.Distinct:
                    builder.Append(_.Distinct).Append(' ');
                    break;
                default:
                    throw new ArgumentOutOfRangeException();
            }
            builder.Append(countFunction.Expr.Accept(this));
            builder.Append(')');
            return builder;
        }

        StringBuilder IFunctionVisitor<StringBuilder>.Visit(IRoundFunction roundFunction)
        {
            var builder = new StringBuilder();
            builder.Append(_.Round);
            builder.Append('(');
            builder.Append(roundFunction.Expr.Accept(this));
            if (!(roundFunction.Decimals is null))
            {
                var decimals = roundFunction.Decimals.Accept(this).ToString();
                if (decimals != "0")
                {
                    builder.Append(", ").Append(decimals);
                }
            }
            builder.Append(')');
            return builder;
        }

        StringBuilder IFunctionVisitor<StringBuilder>.Visit(ISumFunction sumFunction)
        {
            var builder = new StringBuilder();
            builder.Append(_.Sum);
            builder.Append('(');
            switch (sumFunction.AggregateType)
            {
                case AggregateType.None:
                    break;
                case AggregateType.All:
                    builder.Append(_.All).Append(' ');
                    break;
                case AggregateType.Distinct:
                    builder.Append(_.Distinct).Append(' ');
                    break;
                default:
                    throw new ArgumentOutOfRangeException();
            }
            builder.Append(sumFunction.Expr.Accept(this));
            builder.Append(')');
            return builder;
        }

        StringBuilder IFunctionVisitor<StringBuilder>.Visit(IMaxFunction maxFunction)
        {
            var builder = new StringBuilder();
            builder.Append(_.Max);
            builder.Append('(');
            switch (maxFunction.AggregateType)
            {
                case AggregateType.None:
                    break;
                case AggregateType.All:
                    builder.Append(_.All).Append(' ');
                    break;
                case AggregateType.Distinct:
                    builder.Append(_.Distinct).Append(' ');
                    break;
                default:
                    throw new ArgumentOutOfRangeException();
            }
            builder.Append(maxFunction.Expr.Accept(this));
            builder.Append(')');
            return builder;
        }

        StringBuilder IFunctionVisitor<StringBuilder>.Visit(IAvgFunction avgFunction)
        {
            var builder = new StringBuilder();
            builder.Append(_.Avg);
            builder.Append('(');
            switch (avgFunction.AggregateType)
            {
                case AggregateType.None:
                    break;
                case AggregateType.All:
                    builder.Append(_.All).Append(' ');
                    break;
                case AggregateType.Distinct:
                    builder.Append(_.Distinct).Append(' ');
                    break;
                default:
                    throw new ArgumentOutOfRangeException();
            }
            builder.Append(avgFunction.Expr.Accept(this));
            builder.Append(')');
            return builder;
        }

        StringBuilder IFunctionVisitor<StringBuilder>.Visit(IMinFunction minFunction)
        {
            var builder = new StringBuilder();
            builder.Append(_.Min);
            builder.Append('(');
            switch (minFunction.AggregateType)
            {
                case AggregateType.None:
                    break;
                case AggregateType.All:
                    builder.Append(_.All).Append(' ');
                    break;
                case AggregateType.Distinct:
                    builder.Append(_.Distinct).Append(' ');
                    break;
                default:
                    throw new ArgumentOutOfRangeException();
            }
            builder.Append(minFunction.Expr.Accept(this));
            builder.Append(')');
            return builder;
        }

        StringBuilder IFunctionVisitor<StringBuilder>.Visit(IMedianFunction expression)
        {
            return new PercentileFunction(expression.Expr, (NumericExpression) 0.5, PercentileType.Cont, Order.None).Accept(this);
        }

        StringBuilder IFunctionVisitor<StringBuilder>.Visit(IStdDevFunction stdDevFunction)
        {
            var builder = new StringBuilder();
            builder.Append(_.StdDev);
            builder.Append('(');
            switch (stdDevFunction.AggregateType)
            {
                case AggregateType.None:
                    break;
                case AggregateType.All:
                    builder.Append(_.All).Append(' ');
                    break;
                case AggregateType.Distinct:
                    builder.Append(_.Distinct).Append(' ');
                    break;
                default:
                    throw new ArgumentOutOfRangeException();
            }
            builder.Append(stdDevFunction.Expr.Accept(this));
            builder.Append(')');
            return builder;
        }

        StringBuilder IFunctionVisitor<StringBuilder>.Visit(IVarianceFunction varianceFunction)
        {
            var builder = new StringBuilder();
            builder.Append(_.Variance);
            builder.Append('(');
            switch (varianceFunction.AggregateType)
            {
                case AggregateType.None:
                    break;
                case AggregateType.All:
                    builder.Append(_.All).Append(' ');
                    break;
                case AggregateType.Distinct:
                    builder.Append(_.Distinct).Append(' ');
                    break;
                default:
                    throw new ArgumentOutOfRangeException();
            }
            builder.Append(varianceFunction.Expr.Accept(this));
            builder.Append(')');
            return builder;
        }

        StringBuilder IFunctionVisitor<StringBuilder>.Visit(IToCharFunction toCharFunction)
        {
            var builder = new StringBuilder();
            builder.Append(_.To).Append('_').Append(_.Char);
            builder.Append('(');
            builder.Append(toCharFunction.Expr.Accept(this));
            if (!(toCharFunction.Formatter is null))
            {
                builder.Append(", ");
                builder.Append(toCharFunction.Formatter?.Accept(this));
            }

            builder.Append(')');
            return builder;
        }

        StringBuilder IFunctionVisitor<StringBuilder>.Visit(ITrimFunction trimFunction)
        {
            var  builder =  new StringBuilder();
            
            switch (trimFunction.Type)
            {
                case TrimType.Both:
                    builder.Append(_.Trim);
                    break;
                case TrimType.Leading:
                    builder.Append(_.TrimStart);
                    break;
                case TrimType.Trailing:
                    builder.Append(_.TrimEnd);
                    break;
                default:
                    throw new ArgumentOutOfRangeException();
            }
            
            builder.Append('(');
            builder.Append(trimFunction.Expr.Accept(this));
            if (trimFunction.TrimExpr != null)
            {
                builder.Append(", ").Append(trimFunction.TrimExpr.Accept(this));
            }
            builder.Append(')');
            return builder;
        }

        StringBuilder IFunctionVisitor<StringBuilder>.Visit(ILengthFunction lengthFunction)
        {
            var builder = new StringBuilder();
            builder.Append(_.Length);
            builder.Append('(').Append(lengthFunction.Expr.Accept(this)).Append(')');
            return builder;
        }

        StringBuilder IFunctionVisitor<StringBuilder>.Visit(ICastFunction castFunction)
        {
            var builder = new StringBuilder();
            builder.Append(_.Cast);
            builder.Append('(');
            builder.Append(castFunction.Expr.Accept(this));
            builder.Append(' ').Append(_.As).Append(' ');
            builder.Append(castFunction.TargetType);
            builder.Append(')');
            return builder;
        }

        StringBuilder IFunctionVisitor<StringBuilder>.Visit(IToNumberFunction toNumberFunction)
        {
            var  builder = new StringBuilder();
            builder.Append(_.ToNumber);
            builder.Append('(');
            builder.Append(toNumberFunction.Text.Accept(this));
            if (toNumberFunction.Format != null)
            {
                builder.Append(',');
                builder.Append(' ').Append(toNumberFunction.Format.Accept(this));
            }
            builder.Append(')');
            return builder;
        }

        StringBuilder IFunctionVisitor<StringBuilder>.Visit(ISubStringFunction stringFunction)
        {
            var builder = new StringBuilder();
            builder.Append(_.Substr);
            builder.Append('(');
            builder.Append(stringFunction.Text.Accept(this));
            builder.Append(", ").Append(stringFunction.Position.Accept(this));
            if (!(stringFunction.Length is null))
            {
                builder.Append(", ").Append(stringFunction.Length.Accept(this));
            }
            builder.Append(')');
            return builder;
        }

        StringBuilder IFunctionVisitor<StringBuilder>.Visit(INvlFunction nvlFunction)
        {
            var builder = new StringBuilder();
            builder.Append(_.Nvl);
            builder.Append('(');
            builder.Append(nvlFunction.Expr.Accept(this));
            if (!(nvlFunction.Then is null))
            {
                builder.Append(", ").Append(nvlFunction.Then.Accept(this));
            }
            builder.Append(", ").Append(nvlFunction.Default.Accept(this));
            builder.Append(')');
            return builder;
        }

        StringBuilder IFunctionVisitor<StringBuilder>.Visit(IPadFunction padFunction)
        {
            var builder = new StringBuilder();
            switch (padFunction.Type)
            {
                case PaddingType.Left:
                    builder.Append(_.PadLeft);
                    break;
                case PaddingType.Right:
                    builder.Append(_.PadRight);
                    break;
                default:
                    throw new ArgumentOutOfRangeException();
            }
            builder.Append('(');
            builder.Append(padFunction.Text.Accept(this));
            builder.Append(", ");
            builder.Append(padFunction.Length.Accept(this));
            if (!(padFunction.PaddingText is null))
            {
                builder.Append(", ");
                builder.Append(padFunction.PaddingText.Accept(this));
            }
            builder.Append(')');
            return builder;
        }

        StringBuilder IFunctionVisitor<StringBuilder>.Visit(ILeftFunction leftFunction)
        {
            var builder = new StringBuilder();
            builder.Append(_.Left);
            builder.Append('(');
            builder.Append(leftFunction.Text.Accept(this));
            builder.Append(", ").Append(leftFunction.Length.Accept(this));
            builder.Append(')');
            return builder;
        }

        StringBuilder IFunctionVisitor<StringBuilder>.Visit(IRightFunction rightFunction)
        {
            var builder = new StringBuilder();
            builder.Append(_.Right);
            builder.Append('(');
            builder.Append(rightFunction.Text.Accept(this));
            builder.Append(", ").Append(rightFunction.Length.Accept(this));
            builder.Append(')');
            return builder;
        }

        StringBuilder IFunctionVisitor<StringBuilder>.Visit(IUpperFunction upperFunction)
        {
            var builder = new StringBuilder();
            builder.Append(_.Upper);
            builder.Append('(').Append(upperFunction.Text.Accept(this)).Append(')');
            return builder;
        }

        StringBuilder IFunctionVisitor<StringBuilder>.Visit(ILowerFunction lowerFunction)
        {
            var builder = new StringBuilder();
            builder.Append(_.Lower);
            builder.Append('(').Append(lowerFunction.Text.Accept(this)).Append(')');
            return builder;
        }

        StringBuilder IFunctionVisitor<StringBuilder>.Visit(IReplaceFunction replaceFunction)
        {
            var builder = new StringBuilder();
            builder.Append(_.Replace);
            builder.Append('(');
            builder.Append(replaceFunction.Text.Accept(this));
            builder.Append(", ").Append(replaceFunction.OldValue.Accept(this));
            builder.Append(", ").Append(replaceFunction.NewValue.Accept(this));
            builder.Append(')');
            return builder;
        }

        StringBuilder IFunctionVisitor<StringBuilder>.Visit(IExtractFunction extractFunction)
        {
            var builder = new StringBuilder();
            builder.Append(_.Extract);
            builder.Append('(');
            switch (extractFunction.Type)
            {
                case ExtractType.Year:
                    builder.Append(_.Year);
                    break;
                case ExtractType.Month:
                    builder.Append(_.Month);
                    break;
                case ExtractType.Day:
                    builder.Append(_.Day);
                    break;
                case ExtractType.Hour:
                    builder.Append(_.Hour);
                    break;
                case ExtractType.Minute:
                    builder.Append(_.Minute);
                    break;
                case ExtractType.Second:
                    builder.Append(_.Second);
                    break;
                case ExtractType.Week:
                    builder.Append(_.Week);
                    break;
                default:
                    throw new ArgumentOutOfRangeException();
            }

            builder.Append(' ').Append(_.From).Append(' ');
            builder.Append(extractFunction.DateTime.Accept(this));
            builder.Append(')');
            return builder;
        }

        StringBuilder IFunctionVisitor<StringBuilder>.Visit(IConcatFunction concatFunction)
        {
            var builder = new StringBuilder();
            builder.Append(_.Concat);
            builder.Append('(');
            builder.AppendJoin(", ", concatFunction.Items.Select(o => o.Accept(this)));
            builder.Append(')');
            return builder;
        }

        StringBuilder IFunctionVisitor<StringBuilder>.Visit(IReverseFunction reverseFunction)
        {
            var builder = new StringBuilder();
            builder.Append(_.Reverse);
            builder.Append('(');
            builder.Append(reverseFunction.Expr.Accept(this));
            builder.Append(')');
            return builder;
        }

        StringBuilder IFunctionVisitor<StringBuilder>.Visit(IToDatetimeFunction toDatetimeFunction)
        {  
            var builder = new StringBuilder();
            builder.Append(_.To).Append('_').Append(_.Date);
            builder.Append('(');
            builder.Append(toDatetimeFunction.Expr.Accept(this));
            if (!(toDatetimeFunction.Format is null))
            {
                builder.Append(", ");
                builder.Append(toDatetimeFunction.Format?.Accept(this));
            }

            builder.Append(')');
            return builder;
        }

        StringBuilder IFunctionVisitor<StringBuilder>.Visit(IAddIntervalFunction addIntervalFunction)
        {
            var builder = new StringBuilder();
            builder.Append(addIntervalFunction.DateTime.Accept(this));
            builder.Append(' ').Append(addIntervalFunction.Interval.Interval > 0 ? '+' : '-').Append(' ');
            builder.Append(_.Interval);
            builder.Append(' ').Append('\'').Append(Math.Abs(addIntervalFunction.Interval.Interval)).Append('\'').Append(' ');

            switch (addIntervalFunction.Interval.Type)
            {
                case IntervalType.Years:
                    builder.Append(_.Year);
                    break;
                case IntervalType.Months:
                    builder.Append(_.Month);
                    break;
                case IntervalType.Days:
                    builder.Append(_.Day);
                    break;
                case IntervalType.Hours:
                    builder.Append(_.Hour);
                    break;
                case IntervalType.Minutes:
                    builder.Append(_.Minute);
                    break;
                case IntervalType.Seconds:
                    builder.Append(_.Second);
                    break;
                default:
                    throw new ArgumentOutOfRangeException();
            }
            return builder;
        }

        StringBuilder IFunctionVisitor<StringBuilder>.Visit(IStartWithFunction startWithFunction)
        {
            var builder = new StringBuilder();
            builder.Append(startWithFunction.Expr.Accept(this));
            builder.Append(' ').Append(_.Like).Append(' ');
            if (startWithFunction.Pattern is IConstantStringExpression pattern)
            {
                builder.Append('\'').Append(pattern.Value).Append('%').Append('\'');
            }
            else
            {
                throw new NotImplementedException();
            }
            return builder;
        }

        StringBuilder IFunctionVisitor<StringBuilder>.Visit(IEndWithFunction endWithFunction)
        {  
            var builder = new StringBuilder();
            builder.Append(endWithFunction.Expr.Accept(this));
            builder.Append(' ').Append(_.Like).Append(' ');
            if (endWithFunction.Pattern is IConstantStringExpression pattern)
            {
                builder.Append('\'').Append('%').Append(pattern.Value).Append('\'');
            }
            else
            {
                throw new NotImplementedException();
            }
            return builder;
        }

        StringBuilder IFunctionVisitor<StringBuilder>.Visit(IContainsFunction containsFunction)
        {  
            var builder = new StringBuilder();
            builder.Append(containsFunction.Expr.Accept(this));
            builder.Append(' ').Append(_.Like).Append(' ');
            if (containsFunction.Pattern is IConstantStringExpression pattern)
            {
                builder.Append('\'').Append('%').Append(pattern.Value).Append('%').Append('\'');
            }
            else
            {
                throw new NotImplementedException();
            }
            return builder;
        }

        StringBuilder IFunctionVisitor<StringBuilder>.Visit(ISinFunction sinFunction)
        {
            var builder = new StringBuilder();
            builder.Append(_.Sin);
            builder.Append('(');
            builder.Append(sinFunction.Expr.Accept(this));
            builder.Append(')');
            return builder;
        }

        StringBuilder IFunctionVisitor<StringBuilder>.Visit(ICosFunction cosFunction)
        {
            var builder = new StringBuilder();
            builder.Append(_.Cos);
            builder.Append('(');
            builder.Append(cosFunction.Expr.Accept(this));
            builder.Append(')');
            return builder;
        }

        StringBuilder IFunctionVisitor<StringBuilder>.Visit(ITanFunction tanFunction)
        {
            var builder = new StringBuilder();
            builder.Append(_.Tan);
            builder.Append('(');
            builder.Append(tanFunction.Expr.Accept(this));
            builder.Append(')');
            return builder;
        }

        StringBuilder IFunctionVisitor<StringBuilder>.Visit(IAbsFunction absFunction)
        {
            var builder = new StringBuilder();
            builder.Append(_.Abs);
            builder.Append('(');
            builder.Append(absFunction.Expr.Accept(this));
            builder.Append(')');
            return builder;
        }

        StringBuilder IFunctionVisitor<StringBuilder>.Visit(ISignFunction signFunction)
        {
            var builder = new StringBuilder();
            builder.Append(_.Sign);
            builder.Append('(');
            builder.Append(signFunction.Expr.Accept(this));
            builder.Append(')');
            return builder;
        }

        StringBuilder IFunctionVisitor<StringBuilder>.Visit(ISqrtFunction expression)
        {
            var builder = new StringBuilder();
            builder.Append(_.Sqrt);
            builder.Append('(');
            builder.Append(expression.Expr.Accept(this));
            builder.Append(')');
            return builder;
        }

        StringBuilder IFunctionVisitor<StringBuilder>.Visit(ILogFunction expression)
        {
            var builder = new StringBuilder();
            builder.Append(_.Log);

            var hasBase = !(expression.Base is null);

            if (hasBase)
            {
                if (expression.Base is ConstantExpression constantExpression 
                    && (constantExpression.ValueOf().Equals(10) || constantExpression.ValueOf().Equals(10d)))
                {
                    builder.Append("10");
                    hasBase = false;
                }
            }
            
            builder.Append('(');

            if (hasBase)
            {
                builder.Append(expression.Base.Accept(this));

                builder.Append(", ");
            }

            builder.Append(expression.Number.Accept(this));
            builder.Append(')');
            
            return builder;
        }

        StringBuilder IFunctionVisitor<StringBuilder>.Visit(IPowFunction expression)
        {
            var builder = new StringBuilder();
            builder.Append(_.Power);
            builder.Append('(');
            builder.Append(expression.X.Accept(this));
            builder.Append(", ");
            builder.Append(expression.Y.Accept(this));
            builder.Append(')');
            return builder;
        }

        StringBuilder IFunctionVisitor<StringBuilder>.Visit(IRankFunction expression)
        {
            var builder = new StringBuilder();
            if (expression.IsDense)
            {
                builder.Append(_.Dense).Append('_');
            }
            builder.Append(_.Rank).Append('(').Append(')');
            builder.Append(' ').Append(_.Over).Append(' ');
            builder.Append('(');
            builder.Append(_.Order).Append(' ').Append(_.By);
            builder.Append(' ').Append(expression.Expr.Accept(this));

            switch (expression.Order)
            {
                case Order.None:
                    break;
                case Order.Ascending:
                    builder.Append(' ').Append(_.Asc);
                    break;
                case Order.Descending:
                    builder.Append(' ').Append(_.Desc);
                    break;
                default:
                    throw new ArgumentOutOfRangeException();
            }
            builder.Append(')');

            return builder;
        }

        StringBuilder IFunctionVisitor<StringBuilder>.Visit(ICoalesceFunction expression)
        {
            var builder = new StringBuilder(_.Coalesce);
            builder.Append('(').Append(expression.Items.Accept(this)).Append(')');
            return builder;
        }

        StringBuilder IFunctionVisitor<StringBuilder>.Visit(IDecodeFunction expression)
        {
            return expression.ToSimpleMatchExpression().Accept(this);
        }

        StringBuilder IFunctionVisitor<StringBuilder>.Visit(ICeilFunction expression)
        {
            return new StringBuilder(_.Ceil).Append('(').Append(expression.Expr.Accept(this)).Append(')');
        }

        StringBuilder IFunctionVisitor<StringBuilder>.Visit(IFloorFunction expression)
        {
            return new StringBuilder(_.Floor).Append('(').Append(expression.Expr.Accept(this)).Append(')');
        }

        StringBuilder IFunctionVisitor<StringBuilder>.Visit(IPercentileFunction expression)
        {
            var builder = new StringBuilder();
            builder.Append(_.Percentile);
            builder.Append('_');
            switch (expression.Type)
            {
                case PercentileType.Cont:
                    builder.Append(_.Cont);
                    break;
                case PercentileType.Disc:
                    builder.Append(_.Disc);
                    break;
                default:
                    throw new ArgumentOutOfRangeException();
            }

            builder.Append('(').Append(expression.P).Append(')');

            builder.Append(' ').Append(_.Within);
            builder.Append(' ').Append(_.Group);
            builder.Append(' ');
            builder.Append('(');
            builder.Append(_.Order);
            builder.Append(' ');
            builder.Append(_.By);
            builder.Append(' ').Append(expression.Expr.Accept(this));

            switch (expression.Order)
            {
                case Order.None:
                    break;
                case Order.Ascending:
                    builder.Append(' ').Append(_.Asc);
                    break;
                case Order.Descending:
                    builder.Append(' ').Append(_.Desc);
                    break;
                default:
                    throw new ArgumentOutOfRangeException();
            }
            builder.Append(')');

            return builder;
        }

        StringBuilder IFunctionVisitor<StringBuilder>.Visit(IFormatFunction expression)
        {
            var builder = new StringBuilder();

            if (expression.IsDateFormat)
            {
                builder.Append(_.To).Append('_').Append(_.Char);
                builder.Append('(');
                builder.Append(expression.Items[0].Accept(this));
                builder.Append(", ");
                var f = (StringExpression) Settings.DatetimeFormatter.Format(expression.Format.Value);
                builder.Append(f.Accept(this));
                builder.Append(')');
            }
            else
            {
                throw new NotSupportedException();
            }
            return builder;
        }

        StringBuilder IFunctionVisitor<StringBuilder>.Visit(IDiffFunction expression)
        {
            throw new NotImplementedException();
        }

        #endregion
        
        #endregion

        #region [Keywords]

        protected class KeyWords
        {
            public readonly LetterCase LetterCase;

            public KeyWords(LetterCase letterCase)
            {
                LetterCase = letterCase;
            }

            public string FormatLetter(string word)
            {
                if (string.IsNullOrEmpty(word)) return word;
                switch (LetterCase)
                {
                    case LetterCase.None: return word;
                    case LetterCase.UpperCase: return word.ToUpper();
                    case LetterCase.LowerCase: return word.ToLower();
                    case LetterCase.CamelCase: return word.ToCamelCase();
                    case LetterCase.PascalCase: return word.ToPascalCase();
                    default:
                        throw new ArgumentOutOfRangeException();
                }
            }


            public virtual string Add => FormatLetter(nameof(Add));
            public virtual string As => FormatLetter(nameof(As));
            public virtual string Cast => FormatLetter(nameof(Cast));
            public virtual string Ceil => FormatLetter(nameof(Ceil));
            public virtual string Char => FormatLetter(nameof(Char));
            public virtual string Coalesce => FormatLetter(nameof(Coalesce));
            public virtual string Cross => FormatLetter(nameof(Cross));
            public virtual string Date => FormatLetter(nameof(Date));
            public virtual string Diff => FormatLetter(nameof(Diff));
            public virtual string Floor => FormatLetter(nameof(Floor));
            public virtual string Format => FormatLetter(nameof(Format));
            public virtual string From => FormatLetter(nameof(From));
            public virtual string Full => FormatLetter(nameof(Full));
            public virtual string Group => FormatLetter(nameof(Group));
            public virtual string Timestamp => FormatLetter(nameof(Timestamp));
            public virtual string Left => FormatLetter(nameof(Left));
            public virtual string Outer => FormatLetter(nameof(Outer));
            public virtual string Right => FormatLetter(nameof(Right));
            public virtual string Rank => FormatLetter(nameof(Rank));
            public virtual string Over => FormatLetter(nameof(Over));
            public virtual string Dense => FormatLetter(nameof(Dense));
            public virtual string Is => FormatLetter(nameof(Is));
            public virtual string Null => FormatLetter(nameof(Null));
            public virtual string Select => FormatLetter(nameof(Select));
            public virtual string Where => FormatLetter(nameof(Where));
            public virtual string By => FormatLetter(nameof(By));
            public virtual string Order => FormatLetter(nameof(Order));
            public virtual string Log => FormatLetter(nameof(Log));
            public virtual string Now => FormatLetter("Now()");
            public virtual string With => FormatLetter(nameof(With));
            public virtual string Having => FormatLetter(nameof(Having));
            public virtual string Inner => FormatLetter(nameof(Inner));
            public virtual string Skip => FormatLetter(nameof(Skip));
            public virtual string Limit => FormatLetter(nameof(Limit));
            public virtual string Year => FormatLetter(nameof(Year));
            public virtual string Month => FormatLetter(nameof(Month));
            public virtual string Week => FormatLetter(nameof(Week));
            public virtual string Day => FormatLetter(nameof(Day));
            public virtual string Hour => FormatLetter(nameof(Hour));
            public virtual string Minute => FormatLetter(nameof(Minute));
            public virtual string Second => FormatLetter(nameof(Second));
            public virtual string MilliSecond => FormatLetter(nameof(MilliSecond));
            public virtual string Years => FormatLetter(nameof(Years));
            public virtual string Months => FormatLetter(nameof(Months));
            public virtual string Weeks => FormatLetter(nameof(Weeks));
            public virtual string Days => FormatLetter(nameof(Days));
            public virtual string Hours => FormatLetter(nameof(Hours));
            public virtual string Minutes => FormatLetter(nameof(Minutes));
            public virtual string Seconds => FormatLetter(nameof(Seconds));
            public virtual string MilliSeconds => FormatLetter(nameof(MilliSeconds));
            public virtual string Interval => FormatLetter(nameof(Interval));
            public virtual string Join => FormatLetter(nameof(Join));
            public virtual string On => FormatLetter(nameof(On));
            public virtual string In => FormatLetter(nameof(In));
            public virtual string Distinct => FormatLetter(nameof(Distinct));
            public virtual string Not => FormatLetter(nameof(Not));
            public virtual string Case => FormatLetter(nameof(Case));
            public virtual string When => FormatLetter(nameof(When));
            public virtual string End => FormatLetter(nameof(End));
            public virtual string Else => FormatLetter(nameof(Else));
            public virtual string Then => FormatLetter(nameof(Then));
            public virtual string Like => FormatLetter(nameof(Like));
            public virtual string StdDev => FormatLetter(nameof(StdDev));
            public virtual string Variance => FormatLetter(nameof(Variance));
            public virtual string Avg => FormatLetter(nameof(Avg));
            public virtual string Count => FormatLetter(nameof(Count));
            public virtual string Max => FormatLetter(nameof(Max));
            public virtual string Min => FormatLetter(nameof(Min));
            public virtual string Trim => FormatLetter(nameof(Trim));
            public virtual string TrimStart => FormatLetter(nameof(TrimStart));
            public virtual string TrimEnd => FormatLetter(nameof(TrimEnd));
            public virtual string Start => FormatLetter(nameof(Start));
            public virtual string PadLeft => FormatLetter(nameof(PadLeft));
            public virtual string PadRight => FormatLetter(nameof(PadRight));
            public virtual string Asc => FormatLetter(nameof(Asc));
            public virtual string Desc => FormatLetter(nameof(Desc));
            public virtual string And => FormatLetter(nameof(And));
            public virtual string Or => FormatLetter(nameof(Or));
            public virtual string Between => FormatLetter(nameof(Between));
            public virtual string ToNumber => FormatLetter(nameof(ToNumber));
            public virtual string Round => FormatLetter(nameof(Round));
            public virtual string Sum => FormatLetter(nameof(Sum));
            public virtual string Length => FormatLetter(nameof(Length));
            public virtual string Extract => FormatLetter(nameof(Extract));
            public virtual string Upper => FormatLetter(nameof(Upper));
            public virtual string Lower => FormatLetter(nameof(Lower));
            public virtual string Reverse => FormatLetter(nameof(Reverse));
            public virtual string Replace => FormatLetter(nameof(Replace));
            public virtual string Nvl => FormatLetter(nameof(Nvl));
            public virtual string Union => FormatLetter(nameof(Union));
            public virtual string Minus => FormatLetter(nameof(Minus));
            public virtual string Intersect => FormatLetter(nameof(Intersect));
            public virtual string All => FormatLetter(nameof(All));

            public virtual string Sign => FormatLetter(nameof(Sign));
            public virtual string Sin => FormatLetter(nameof(Sin));
            public virtual string Cos => FormatLetter(nameof(Cos));
            public virtual string Tan => FormatLetter(nameof(Tan));
            public virtual string Abs => FormatLetter(nameof(Abs));
            public virtual string Concat => FormatLetter(nameof(Concat));
            public virtual string Substr => FormatLetter(nameof(Substr));

            public virtual string Exists => FormatLetter(nameof(Exists));
            public virtual string Sqrt => FormatLetter(nameof(Sqrt));
            public virtual string Power => FormatLetter(nameof(Power));
            public virtual string Percentile => FormatLetter(nameof(Percentile));
            public virtual string Cont => FormatLetter(nameof(Cont));
            public virtual string Disc => FormatLetter(nameof(Disc));
            public virtual string Within => FormatLetter(nameof(Within));
            public virtual string Median => FormatLetter(nameof(Median));
            public virtual string To => FormatLetter(nameof(To));
            public virtual string Decimal => FormatLetter(nameof(Decimal));
            public virtual string Numeric => FormatLetter(nameof(Numeric));

            public virtual string Both => FormatLetter(nameof(Both));
            public virtual string Leading => FormatLetter(nameof(Leading));
            public virtual string Trailing => FormatLetter(nameof(Trailing));
            public virtual string Varchar => FormatLetter(nameof(Varchar));
        }

        #endregion
    }
}